package com.oanda.blackberry.view;

import java.util.Calendar;
import java.util.Date;

import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Graphics;

import com.oanda.blackberry.restapi.PricePoint;
import com.oanda.blackberry.util.DefaultController;
import com.oanda.blackberry.util.Model;
import com.oanda.blackberry.util.StringUtils;

public class ChartField extends AbstractChartField {
    int fontHeight = 12;
    Font font = DefaultController.getComponentFont().derive(0, this.fontHeight);

    public ChartField(PricePoint[] points, String symbol, int precious, int chartType) {
        this.points = points;
        this.symbol = symbol;
        this.precious = precious;
        AbstractChartField.chartType = chartType;
    }

    private String format(int i) {
        if (i <= 9) {
            return "0" + String.valueOf(i);
        }
        return String.valueOf(i);
    }

    public void updateGraphData(PricePoint[] newUpdatedPoints) {
        if ((newUpdatedPoints != null) && (newUpdatedPoints.length > 1)) {
            update(newUpdatedPoints[0], newUpdatedPoints[1]);

            invalidate();
        }
    }

    public void update(PricePoint p1, PricePoint p2) {
        if ((this.points != null) && (this.points.length > 0) && (p1 != null) && (p2 != null))
            if (p2.time == this.points[(this.points.length - 1)].time) {
                this.points[(this.points.length - 1)] = p2;
            } else
                insertElementAtEnd(p1, p2);
    }

    private void insertElementAtEnd(PricePoint lastStableElement, PricePoint newElement) {
        PricePoint[] temp = this.points;
        for (int i = 0; i < temp.length - 1 - 1; i++) {
            this.points[i] = temp[(i + 1)];
        }
        this.points[(this.points.length - 1)] = newElement;
        this.points[(this.points.length - 2)] = lastStableElement;
    }

    protected void paint(Graphics graphics) {
        int topPadding = 15;
        int bottomPadding = 20;
        int rightPadding = 10;
        int leftPadding = 20;

        int yScale = 0;
        int pointSize = 1;
        boolean showLine = true;

        graphics.setFont(this.font);
        graphics.setColor(0);

        int xTickIndent = 2;
        int yTickIndent = 2;

        int yTickSpace = 5;
        int xTickSpace = 7;

        int labelWidth = 0;
        switch (AbstractChartField.chartType) {
        case 2:
            labelWidth = this.font.getAdvance(Float.toString(this.points[0].maxBid));
            break;
        case 3:
            labelWidth = this.font.getAdvance(Float.toString(this.points[0].minBid));
            break;
        case 0:
            labelWidth = this.font.getAdvance(Float.toString(this.points[0].minBid));
            break;
        case 1:
            labelWidth = this.font.getAdvance(Float.toString(this.points[0].minBid));
        }

        int plotWidth = this.width - (yTickIndent + 6) - labelWidth - 2 - rightPadding
                - leftPadding;
        int plotHeight = this.height - xTickIndent - 2 - bottomPadding - topPadding;

        graphics.setColor(11393254);

        graphics.setColor(0);
        graphics.fillRect(0 + leftPadding, 0 + topPadding, plotWidth, plotHeight);

        yScale = plotHeight;

        float minY = 0.0F;
        float maxY = 0.0F;
        float dMaxY = 0.0F;

        float minYmin = 0.0F;
        float maxYmin = 0.0F;
        float dMaxYmin = 0.0F;

        long minX = 0L;
        long maxX = 0L;
        long dMaxX = 0L;

        switch (AbstractChartField.chartType) {
        case 2:
            minY = getMin(3);
            maxY = getMax(3);
            dMaxY = maxY - minY;

            minYmin = 0.0F;
            maxYmin = 0.0F;
            dMaxYmin = maxYmin - minYmin;

            minX = getMinTime();
            maxX = getMaxTime();
            dMaxX = maxX - minX;

            break;
        case 3:
            minY = getMin(1);
            maxY = getMax(1);
            dMaxY = maxY - minY;

            minYmin = 0.0F;
            maxYmin = 0.0F;
            dMaxYmin = maxYmin - minYmin;

            minX = getMinTime();
            maxX = getMaxTime();
            dMaxX = maxX - minX;
            break;
        case 0:
            minY = getMin(10);
            maxY = getMax(10);
            dMaxY = maxY - minY;

            minYmin = getMin(9);
            maxYmin = getMax(9);
            dMaxYmin = maxYmin - minYmin;

            minX = getMinTime();
            maxX = getMaxTime();
            dMaxX = maxX - minX;
            break;
        case 1:
            minY = getMin(12);
            maxY = getMax(12);
            dMaxY = maxY - minY;

            minYmin = getMin(11);
            maxYmin = getMax(11);
            dMaxYmin = maxYmin - minYmin;

            minX = getMinTime();
            maxX = getMaxTime();
            dMaxX = maxX - minX;
        }

        graphics.setColor(32768);
        float tempY = 0.0F;
        float tempYmin = 0.0F;

        for (int count = yTickSpace; count >= 0; count--) {
            graphics.setColor(8421504);
            if (count == 0)
                graphics.fillRect(leftPadding, topPadding + (int) tempY + 1, plotWidth, 1);
            else {
                graphics.fillRect(leftPadding, topPadding + (int) tempY, plotWidth, 1);
            }

            graphics.setColor(8421504);

            if (count == 0)
                graphics.fillRect(leftPadding + plotWidth, (int) (topPadding + tempY) + 1, 6, 1);
            else {
                graphics.fillRect(leftPadding + plotWidth, (int) (topPadding + tempY), 6, 1);
            }

            float f = minY + dMaxY / yTickSpace * count;
            graphics.drawText(formatFloat(f, this.precious), leftPadding + plotWidth + 6
                    + yTickIndent, (int) (topPadding + tempY) - this.fontHeight / 2);

            tempY += plotHeight / yTickSpace;
        }

        float tempX = plotWidth;

        tempY = plotHeight;

        tempY = topPadding + plotHeight - 2;

        tempX += leftPadding;

        Calendar c = Calendar.getInstance();

        for (int count = xTickSpace - 1; count >= 0; count--) {
            int index = count * this.points.length / (xTickSpace - 1);
            try {
                c.setTime(new Date(this.points[index].time * 1000L));
            } catch (ArrayIndexOutOfBoundsException i) {
                c.setTime(new Date(this.points[(index - 1)].time * 1000L));
            } catch (IndexOutOfBoundsException i) {
                c.setTime(new Date(this.points[(index - 1)].time * 1000L));
            }

            String s = format(c.get(10)) + ":" + format(c.get(12));
            if (Model.getModel().getSettings().getHistoryTimeInterval().indexOf("1 da") > -1) {
                s = format(c.get(2)) + "/" + format(c.get(5));
            }

            if ((count != xTickSpace - 1) && (count != 0)) {
                graphics.drawText(s, (int) tempX - this.font.getAdvance("00|"), (int) tempY + 2
                        + this.fontHeight / 2);

                graphics.drawLine((int) tempX, topPadding, (int) tempX, (int) tempY + 2);
            } else if (count == 0) {
                index += 4;
                graphics.drawText(s, (int) tempX - this.font.getAdvance("00|") + 10, (int) tempY
                        + 2 + this.fontHeight / 2);

                graphics.drawLine((int) tempX + 10, topPadding, (int) tempX + 10, (int) tempY + 2);
            } else if (count == xTickSpace - 1) {
                index -= 4;
                graphics.drawText(s, (int) tempX - this.font.getAdvance("00|") - 20, (int) tempY
                        + 2 + this.fontHeight / 2);

                graphics.drawLine((int) tempX - 20, topPadding, (int) tempX - 20, (int) tempY + 2);
            }

            tempX -= plotWidth / (xTickSpace - 1);
        }

        float lastX = 0.0F;
        float lastY = 0.0F;
        float lastXmin = 0.0F;
        float lastYmin = 0.0F;

        graphics.setColor(16711680);

        int pointCenterTweak = pointSize >> 1;

        tempX = plotWidth;
        boolean firstPoint = true;

        tempX = 0.0F;
        tempYmin = 0.0F;

        float tempYOpen = 0.0F;
        float tempYClose = 0.0F;

        int[] xp = new int[this.points.length];
        int[] yp = new int[this.points.length];
        int[] yp1 = new int[this.points.length];
        graphics.setDrawingStyle(1, true);
        graphics.setDrawingStyle(2, true);
        for (int count2 = 0; count2 < this.points.length; count2++) {
            tempX = leftPadding + 1 + count2 * plotWidth / this.points.length;

            switch (AbstractChartField.chartType) {
            case 3:
                tempY = topPadding + plotHeight - (this.points[count2].minBid - minY) / dMaxY
                        * yScale;
                break;
            case 2:
                tempY = topPadding + plotHeight - (this.points[count2].maxBid - minY) / dMaxY
                        * yScale;
                break;
            case 0:
                tempY = topPadding
                        + plotHeight
                        - (Math.max(this.points[count2].maxBid, this.points[count2].maxAsk) - minYmin)
                        / (maxY - minYmin) * yScale;
                tempYmin = topPadding
                        + plotHeight
                        - (Math.min(this.points[count2].minAsk, this.points[count2].minBid) - minYmin)
                        / (maxY - minYmin) * yScale;
                yp[count2] = (int) tempY;
                yp1[count2] = (int) tempYmin;
                xp[count2] = (int) tempX;
                break;
            case 1:
                tempY = topPadding + plotHeight - (this.points[count2].getMaxPriceAvr() - minYmin)
                        / (maxY - minYmin) * yScale;
                tempYmin = topPadding + plotHeight
                        - (this.points[count2].getMinPriceAvr() - minYmin) / (maxY - minYmin)
                        * yScale;

                tempYOpen = topPadding + plotHeight
                        - (this.points[count2].getOpenPriceAvr() - minYmin) / (maxY - minYmin)
                        * yScale;
                tempYClose = topPadding + plotHeight
                        - (this.points[count2].getClosePriceAvr() - minYmin) / (maxY - minYmin)
                        * yScale;
            }

            if (pointSize > 0) {
                graphics.fillArc((int) tempX - pointCenterTweak, (int) tempY - pointCenterTweak,
                        pointSize, pointSize, 0, 360);
            }

            if (showLine) {
                if (!firstPoint) {
                    if ((AbstractChartField.chartType != 1) && (AbstractChartField.chartType != 0)) {
                        graphics.drawLine((int) tempX, (int) tempY, (int) lastX, (int) lastY);
                    }

                    if (tempYmin != 0.0F) {
                        if ((AbstractChartField.chartType != 1)
                                && (AbstractChartField.chartType != 0)) {
                            graphics.setColor(255);
                            graphics.drawLine((int) tempX, (int) tempYmin, (int) lastX,
                                    (int) lastYmin);
                        }

                        if (AbstractChartField.chartType == 1) {
                            graphics.setColor(16119285);
                            graphics.drawLine((int) tempX, (int) tempY, (int) tempX, (int) tempYmin);
                        }

                        switch (AbstractChartField.chartType) {
                        case 0:
                            break;
                        case 1:
                            if (tempYOpen > tempYClose)
                                graphics.setColor(32768);
                            else {
                                graphics.setColor(16711680);
                            }

                            int barW = 7;
                            int barH = (int) (Math.max(tempYClose, tempYOpen) - Math.min(
                                    tempYClose, tempYOpen));
                            if (barH <= 0) {
                                barH = 1;
                            }
                            graphics.fillRect((int) tempX - barW / 2,
                                    (int) Math.min(tempYOpen, tempYClose), barW, barH);

                            graphics.setColor(16119285);
                            graphics.drawRect((int) tempX - barW / 2,
                                    (int) Math.min(tempYOpen, tempYClose), barW, barH);
                        }

                    }

                }

                lastX = tempX;
                lastY = tempY;
                lastYmin = tempYmin;
                firstPoint = false;
            }

        }

        if (AbstractChartField.chartType == 0) {
            graphics.setDrawingStyle(2, true);

            graphics.setGlobalAlpha(180);
            graphics.setColor(16119285);

            int[] xptemp = new int[xp.length];
            System.arraycopy(xp, 0, xptemp, 0, xp.length);
            int[] yptemp = new int[yp.length];
            System.arraycopy(yp, 0, yptemp, 0, yp.length);
            reverse(xp);
            reverse(yp1);
            int[] cxp = concat(xptemp, xp);
            int[] cyp = concat(yptemp, yp1);
            graphics.drawFilledPath(cxp, cyp, createPointTypesArray(cxp.length), null);
            graphics.setColor(8421504);

            graphics.setDrawingStyle(2, false);
        }
    }

    public static void reverse(int[] b) {
        int left = 0;
        int right = b.length - 1;

        while (left < right) {
            int temp = b[left];
            b[left] = b[right];
            b[right] = temp;

            left++;
            right--;
        }
    }

    int[] concat(int[] A, int[] B) {
        int[] C = new int[A.length + B.length];
        System.arraycopy(A, 0, C, 0, A.length);
        System.arraycopy(B, 0, C, A.length, B.length);

        return C;
    }

    byte[] createPointTypesArray(int len) {
        byte[] b = new byte[len];
        for (int i = 0; i < b.length; i++) {
            if (i % 2 == 0)
                b[i] = 0;
            else {
                b[i] = 1;
            }
        }
        return b;
    }

    static int calcFormatPecious(float[] values) {
        int pre = 0;
        for (int i = 0; i < values.length; i++) {
            String s = Float.toString(values[i]);
            if (s.length() > pre) {
                pre = s.length();
            }
        }
        return pre;
    }

    static String formatFloat(float f, int len) {
        return StringUtils.formatDouble(f, len);
    }

    static long getMinXScale(long[] values) {
        long min = values[0];
        for (int i = 0; i < values.length; i++) {
            if (values[i] < min) {
                min = values[i];
            }
        }
        return min;
    }

    static float getMinYScale(float[] values) {
        if (values == null) {
            return 0.0F;
        }
        float min = values[0];
        for (int i = 0; i < values.length; i++) {
            if (values[i] < min) {
                min = values[i];
            }
        }
        return min;
    }

    static float getMaxYScale(float[] values) {
        if (values == null) {
            return 0.0F;
        }
        float max = values[0];
        for (int i = 0; i < values.length; i++) {
            if (values[i] > max) {
                max = values[i];
            }
        }
        return max;
    }

    static long getMaxXScale(long[] values) {
        long max = values[0];
        for (int i = 0; i < values.length; i++) {
            if (values[i] > max) {
                max = values[i];
            }
        }
        return max;
    }
}
